package com.leyou.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.leyou.constants.SearchConstants;
import com.leyou.dto.*;
import com.leyou.entity.Goods;
import com.leyou.item.client.ItemClient;
import com.leyou.myException.LyException;
import com.leyou.repository.GoodsRepository;
import com.leyou.service.SearchService;
import com.leyou.starter.elastic.dto.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.index.Term;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static com.leyou.constants.SearchConstants.*;

/**
 * @author jshstart
 * @creat 2020-12-15 21:43
 */
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsRepository repository;
    @Autowired
    private ItemClient itemClient;
    @Autowired
    private RestHighLevelClient client;

    /**
     * 创建索引库
     */
    @Override
    public void createIndexAndMapping() {
        try {
            repository.deleteIndex();
        } catch (Exception e) {
            log.error("删除失败，索引库不存在");
        }
        repository.createIndex(SearchConstants.CREATE_INDEX);
    }
/*
导入数据
 */
    @Override
    public void loadData() {
//        int page = 1;
//        int rows = 100;
//        while (true) {
        //page的值可以自动赋到{}里面
//            log.info("开始导入第{}页数据", page);
//            // 分页查询已经上架的spu
//            PageDTO<SpuDTO> result = itemClient.querySpuByPage(page, rows, true, null, null, null);
//            List<SpuDTO> spuList = result.getItems();
//            List<Goods> goodsList = spuList.stream().map(this::buildGoods).collect(Collectors.toList());
//        if (goodsList!=null){
//            repository.saveAll(goodsList);
//        }
//            log.info("导入第{}页数据结束" + page);
//            page++;
//            Long totalPage = result.getTotalPage();
//            if (page > totalPage) {
//                break;
//            }
//        }
//        int page = 1, rows = 100;
//        while (true) {
//            log.info("开始导入第{}页数据", page);
//            // 分页查询已经上架的spu
//            PageDTO<SpuDTO> result = itemClient.querySpuByPage(page,rows, true, null, null, null);
//            List<SpuDTO> list = result.getItems();
//
//            // 遍历Spu集合，把SpuDTO通过buildGoods方法转为Goods
//            List<Goods> goodsList = list.stream()
//                    .map(this::buildGoods).collect(Collectors.toList());
//
//            // 批量写入Elasticsearch
//            if (goodsList!=null&&goodsList.size()>0){
//                repository.saveAll(goodsList);
//            }
//            log.info("导入第{}页数据结束。", page);
//            // 翻页
//            page++;
//
//            // 获取总页数
//            Long totalPage = result.getTotalPage();
////            totalPage=totalPage+1;
//            // 判断是否还有spu没有查询
//            if(page > totalPage){
//                // 没有则结束
//                break;
//            }
//        }
        int page=1;int rows=100;
        while (true) {
            PageDTO<SpuDTO> spuDTOPage = itemClient.querySpuByPage(page, rows, true, null, null, null);
            List<SpuDTO> spuDTOS = spuDTOPage.getItems();
            List<Goods> goodsList = spuDTOS.stream().map(this::buildGoods).collect(Collectors.toList());
            log.info("第{}页开始下载",page);
            if (goodsList!=null&&goodsList.size()>0) {
                repository.saveAll(goodsList);
            }
            log.info("第{}页下载结束",page);
            page++;
            if (page>spuDTOPage.getTotalPage()){
                break;
            }
        }
    }

/*
自动补全查询
 */
    @Override
    public Mono<List<String>> queryBySuggest(String key) {
        if (StringUtils.isEmpty(key)){
            throw  new LyException(400,"请求参数不能为空");
        }
        Mono<List<String>> listMono = repository.suggestBySingleField(SUGGESTION_FIELD,key);
        return listMono;
    }
/*
根据分页显示搜索出来的商品
 */
    @Override
    public Mono<PageInfo<Goods>> searchByPage(SearchParamDTO searchParamDTO) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //进行过滤
        sourceBuilder.fetchSource(DEFAULT_SOURCE_FIELD,new String[0]);
        if (StringUtils.isEmpty(searchParamDTO.getKey())){throw  new LyException(400,"没有查询条件");}
        //默认从标题查询
        sourceBuilder.query(QueryBuilders.matchQuery(DEFAULT_SEARCH_FIELD,searchParamDTO.getKey()));
        //设置分页
        sourceBuilder.from(searchParamDTO.getFrom());
        sourceBuilder.size(searchParamDTO.getSize());
        //设置排序
        if (!StringUtils.isEmpty(searchParamDTO.getSortBy())){
            sourceBuilder.sort(searchParamDTO.getSortBy(),searchParamDTO.getDesc()? SortOrder.DESC:SortOrder.ASC);
        }
        //设置高亮条件
        sourceBuilder.highlighter(new HighlightBuilder().field(DEFAULT_SEARCH_FIELD).preTags(DEFAULT_PRE_TAG).postTags(DEFAULT_POST_TAG));
        return  repository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    /**
     * 根据聚合过滤页面顶部条件
     * @param searchParamDTO
     * @return
     */
    /*
     docker run -di \
> --name=ly-redis \
> -p 6379:6379 redis \
> -v $PWD/redis.conf:/usr/local/etc/redis/redis.conf \
> --privileged \
> redis-server /usr/local/etc/redis/redis.conf
docker run -di --name=ly-redis -p 6379:6379 redis \
-v $PWD/redis.conf:/usr/local/etc/redis/redis.conf \
redis-server /usr/local/etc/redis/redis.conf
     */
    @Override
    public  Map<String,List<?>>  searchByAgg(SearchParamDTO searchParamDTO) throws IOException {
      Map<String,List<?>> map=  new HashMap();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //进行过滤
        sourceBuilder.fetchSource(DEFAULT_SOURCE_FIELD,new String[0]);
        if (StringUtils.isEmpty(searchParamDTO.getKey())){throw  new LyException(400,"没有查询条件");}
        //默认从标题查询
        sourceBuilder.query(QueryBuilders.matchQuery(DEFAULT_SEARCH_FIELD,searchParamDTO.getKey()));
        //根据分组id进行聚合
        String key = searchParamDTO.getKey();
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId"));
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId"));
        sourceBuilder.aggregation(AggregationBuilders.nested("specAgg", "specs")
                .subAggregation(AggregationBuilders.terms("NAMEAgg").field("specs.name")
                .subAggregation(AggregationBuilders.terms("ValueAgg").field("specs.value"))));
        SearchRequest request = new SearchRequest("goods");
        request.source(sourceBuilder);
        //发送请求，获得结果集
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //拿到结果集中的聚合结果
            Aggregations aggregations = response.getAggregations();
            //获取分类聚合的结果，拿到分类数据
        Terms categoryAgg = aggregations.get("categoryAgg");
        //获得分类集合的桶
        List<? extends Terms.Bucket> categoryAggBuckets = categoryAgg.getBuckets();
        ArrayList<Map<String, Object>> categoryList = new ArrayList<>();
        for (Terms.Bucket categoryBucket : categoryAggBuckets) {
            HashMap<String, Object> categoryMap = new HashMap<>();
            String categoryKey = categoryBucket.getKeyAsString();
            CategoryDTO categoryDTO = itemClient.queryCategoryById((long) Integer.parseInt(categoryKey));
            String name = categoryDTO.getName();
            categoryMap.put("id",(long) Integer.parseInt(categoryKey));
            categoryMap.put("name",name);
            categoryList.add(categoryMap);
        }
        map.put("分类",categoryList);
        //2.获取品牌的聚合结果
        Terms brandAgg = aggregations.get("brandAgg");
        //获取品牌的桶的结果
        List<? extends Terms.Bucket> brandAggBuckets = brandAgg.getBuckets();
        ArrayList<Map<String, Object>> brandList = new ArrayList<>();
        for (Terms.Bucket brandAggBucket : brandAggBuckets) {
            HashMap<String, Object> brandMap = new HashMap<>();
            String brandKey = brandAggBucket.getKeyAsString();
            BrandDTO brandDTO = itemClient.queryBrandById((long) Integer.parseInt(brandKey));
            String image = brandDTO.getImage();
            String name = brandDTO.getName();
            brandMap.put("id",(long) Integer.parseInt(brandKey));
            brandMap.put("name",name);
            brandMap.put("image",image);
            brandList.add(brandMap);
        }
        map.put("品牌",brandList);
        //3.规格参数
        Nested specAgg = aggregations.get("specAgg");
        Terms nameAgg = specAgg.getAggregations().get("NAMEAgg");
        List<? extends Terms.Bucket> nameAggBuckets = nameAgg.getBuckets();
        for (Terms.Bucket nameAggBucket : nameAggBuckets) {
            String fatherName = nameAggBucket.getKeyAsString();
            Terms valueAgg = nameAggBucket.getAggregations().get("ValueAgg");
            List<? extends Terms.Bucket> valueBuckets = valueAgg.getBuckets();
            List childrenNameList= new ArrayList<String>();
            for (Terms.Bucket valueBucket : valueBuckets) {
                String childrenName = valueBucket.getKeyAsString();
                childrenNameList.add(childrenName);
            }
            map.put(fatherName,childrenNameList);

        }
        return map;
    }
    /**
     * 根据spuId增加商品
     * @param spuId
     */
    @Override
    public void saveGoodBySpuId(Long spuId) {
        SpuDTO spuDTO = itemClient.queryGoodsById(spuId);
        Goods goods = buildGoods(spuDTO);
        repository.save(goods);
    }

    /**
     * 根据spuId删除数据
     * @param spuId
     */
    @Override
    public void deleteGoodBySpuId(Long spuId) {
        repository.deleteById(spuId);
    }



    /*
    构建goods对象
     */
    private Goods buildGoods(SpuDTO spu) {
//        // 1.自动补全的提示字段
//        List<String> suggestion = new ArrayList<>(
//                Arrays.asList(StringUtils.split(spu.getCategoryName(), "/")));
//        suggestion.add(spu.getName());
//        suggestion.add(spu.getBrandName());
//
//        // 2.sku的价格集合
//        // 2.1.查询sku集合
//        List<SkuDTO> skuList = spu.getSkus();
//        if (CollectionUtils.isEmpty(skuList)) {
//            // 没有sku，我们去查询
//            skuList = itemClient.querySkuBySpuId(spu.getId());
//        }
//        // 2.2.获取价格集合
//        Set<Long> prices = skuList.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());
//
//        // 3.商品销量
//        long sold = skuList.stream().mapToLong(SkuDTO::getSold).sum();
//
//        // 4.sku的某个图片
//        String image = StringUtils.substringBefore(skuList.get(0).getImages(), ",");
//
//        // 5.规格参数
//        List<Map<String, Object>> specs = new ArrayList<>();
//        // 5.1.查询规格参数name和value键值对，只查询参与搜索的
//        List<SpecParamDTO> params = itemClient.querySpecsValues(spu.getId(), true);
//        // 5.2.封装
//        for (SpecParamDTO param : params) {
//            Map<String, Object> map = new HashMap<>(2);
//            map.put("name", param.getName());
//            specs.add(map);
//            map.put("value", chooseSegment(param));
//        }
//
//        // 创建Goods对象，并封装数据
//        Goods goods = new Goods();
//        goods.setUpdateTime(new Date());
//        // 自动补全的提示字段
//        goods.setSuggestion(suggestion);
//        // 规格参数
//        goods.setSpecs(specs);
//        // 商品销量
//        goods.setSold(sold);
//        // 商品标题
//        goods.setTitle(spu.getTitle() + StringUtils.join(suggestion, " "));
//        // sku的价格集合
//        goods.setPrices(prices);
//        // sku的某个图片
//        goods.setImage(image);
//        goods.setCategoryId(spu.getCid3());
//        goods.setBrandId(spu.getBrandId());
//        goods.setId(spu.getId());
//        return goods;
        //1.构建标题
        String title=spu.getTitle()+spu.getBrandName()+spu.getCategoryName();
        //2.构建自动补充查询条件
        ArrayList<String> suggestion = new ArrayList<>();
        suggestion.add(spu.getName());
        suggestion.add(spu.getBrandName());
        Collections.addAll(suggestion,StringUtils.split(spu.getCategoryName(),"/"));
        //3.构建规格参数
        List<SpecParamDTO> specList = itemClient.querySpecsValues(spu.getId(), true);
        List<Map<String,Object>> spec=new ArrayList<>(specList.size());
        for (SpecParamDTO param : specList) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name",param.getName());
            map.put("value",chooseSegment(param));
            spec.add(map);
        }
        //4.获取sku，获得销量，价格，图片
        List<SkuDTO> skus = spu.getSkus();
        if (CollectionUtils.isEmpty(skus)){
            skus=itemClient.querySkuBySpuId(spu.getId());
        }
        Long sold=0L;
        Set<Long> price=new HashSet<>();
        for (SkuDTO skuDTO : skus) {
            //销量
            sold+=skuDTO.getSold();
            //价格
            price.add(skuDTO.getPrice());
        }
        String images = skus.get(0).getImages();
        String img = StringUtils.substringBefore(images, ",");
        Goods goods = new Goods();
        goods.setUpdateTime(new Date());
        goods.setTitle(title);
        goods.setSuggestion(suggestion);
        goods.setSpecs(spec);
        goods.setSold(sold);
        goods.setPrices(price);
        goods.setImage(img);
        goods.setId(spu.getId());
        goods.setCategoryId(spu.getCid3());
        goods.setBrandId(spu.getBrandId());
        return goods;
    }
    /*
    把value的值进行判断，把范围作为值传进去
     */
    private Object chooseSegment(SpecParamDTO p) {
        Object value = p.getValue();
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        if(!p.getNumeric() || StringUtils.isBlank(p.getSegments()) || value instanceof Collection){
            return value;
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }
}
